package com.example.database.demo.service.Impl;

import cn.hutool.core.util.ObjectUtil;
import com.example.database.demo.mapper.PosPostOrderMapper;
import com.example.database.demo.pojo.JewelryPojo.RequestEncodedData;
import com.example.database.demo.pojo.JewelryPojo.requestData.Cancellnsure.CancellnsureParam;
import com.example.database.demo.pojo.JewelryPojo.requestData.Insure.InsureParam;
import com.example.database.demo.pojo.JewelryPojo.responseData.Body;
import com.example.database.demo.pojo.JewelryPojo.responseData.Response;
import com.example.database.demo.pojo.JewelryPojo.responseData.ResponseData;
import com.example.database.demo.service.JewelryCargoInsuranceService;
import com.example.database.demo.utils.RSASignUtil;
import com.example.database.demo.utils.RSAUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @author xionglin
 */
@Slf4j
@Service
public class JewelryCargoInsuranceServiceImpl implements JewelryCargoInsuranceService {
    @Value("${rsa.api.in.pub.key}")
    private String api_in_pub_key;
    @Value("${rsa.api.out.pub.key}")
    private String api_out_pub_key;
    @Value("${rsa.api.out.pri.key}")
    private String api_out_pri_key;
    @Value("${rsa_algorithm}")
    private String algorithm;
    @Value("${rsa_encode}")
    private String encode;
    @Value("${JEWELRY.PROPOSAL}")
    private String jewelarProposalUrl;
    @Value("${JEWELRY.ENDOR}")
    private String jewelarEndorUrl;
    @Value("${JEWELRY.USERCODE}")
    private String USERCODE;
    @Value("${JEWELRY.PASSWORD}")
    private String PASSWORD;
    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Autowired
    private RetryTemplate retryTemplate;
    @Autowired
    private PosPostOrderMapper posPostOrderMapper;

    @Override
    public List<ResponseData> doInsure(List<InsureParam> insureParamList) {
        List<ResponseData> responseList = new CopyOnWriteArrayList<>();
        ArrayList<CompletableFuture<Void>> futures = new ArrayList<>();
        for (InsureParam insureParam : insureParamList) {
            CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {
                try {
                    insureParam.getRequest().getHead().setUserCode(USERCODE);
                    insureParam.getRequest().getHead().setPassword(PASSWORD);
                    return retryTemplate.execute(context -> {
                        ResponseData responseData = doOneInsure(insureParam);
                        posPostOrderMapper.upInsureInfo(responseData.getResponse().getHead().getTransNo(), responseData.getResponse().getBody().getPolicyNo(), String.valueOf(responseData.getResponse().getBody().getSumPremium()));
                        return responseData;
                    });
                } catch (Exception e) {
                    ResponseData responseData = new ResponseData();
                    Response response = new Response();
                    Body body = new Body();
                    body.setErrorCode("500");
                    body.setPolicyNo("");
                    body.setErrorMsg(e.getMessage());
                    response.setBody(body);
                    responseData.setResponse(response);
                    return responseData;
                }
            }, threadPoolTaskExecutor).thenAccept(responseList::add);
            futures.add(future);
        }
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
        return responseList;
    }

    public ResponseData doOneInsure(InsureParam insureParam) throws Exception {
        log.info("Async task running in thread: {}", Thread.currentThread().getName());
        RequestEncodedData requestData = new RequestEncodedData();
        //开始组装JSON数据
        String valueAsString = objectMapper.writeValueAsString(insureParam);
        log.info("入参加密前:{}", valueAsString);
        //将入参加密
        String encodedData = RSAUtils.privateEncrypt(valueAsString, RSAUtils.getPrivateKey(api_out_pri_key));
        log.info("入参加密后:{}", encodedData);
        String sign = RSASignUtil.sign(encodedData, api_out_pri_key, algorithm, encode);
        //组装请求报文参数
        requestData.setSecretKey(encodedData);
        requestData.setSign(sign);
        String requestEncodedData = objectMapper.writeValueAsString(requestData);
        //开始发送请求
        log.info("开始投保,投保单号====>{}", insureParam.getRequest().getHead().getTransNo());
        String doPostResult = doPost(requestEncodedData, jewelarProposalUrl);
        log.info("响应报文解密前:{}", doPostResult);
        RequestEncodedData responseData = objectMapper.readValue(doPostResult, RequestEncodedData.class);
        String secretKey = responseData.getSecretKey();
        //开始对响应报文的secretKey解密
        String decodedData = RSAUtils.publicDecrypt(secretKey, RSAUtils.getPublicKey(api_in_pub_key));
        log.info("响应报文解密后:{}", decodedData);
        //组装本地响应数据
        ResponseData data = objectMapper.readValue(decodedData, ResponseData.class);
        String signResult = RSASignUtil.sign(secretKey, api_out_pri_key, algorithm, encode);
        log.info("响应报文签名后:{}", sign);
        boolean check = RSASignUtil.verify(secretKey, signResult, api_out_pub_key, algorithm, encode);
        log.info("校验签名:{}\n", check);
        if (ObjectUtil.notEqual(data.getResponse().getBody().getErrorCode(), "0") || ObjectUtil.notEqual(data.getResponse().getBody().getUnderWriteInd(), "6")) {
            log.info("投保出现异常,开始重新投保,投保单号====>{}", insureParam.getRequest().getHead().getTransNo());
            throw new RuntimeException(data.getResponse().getBody().getErrorMsg());
        }
        return data;
    }

    @Override
    public ResponseData doCancelInsure(CancellnsureParam cancellnsureParam) throws JsonProcessingException, NoSuchAlgorithmException, InvalidKeySpecException {
        RequestEncodedData requestData = new RequestEncodedData();
        //开始组装JSON数据
        String valueAsString = objectMapper.writeValueAsString(cancellnsureParam);
        log.info("入参加密前:{}", valueAsString);
        //将入参加密
        String encodedData = RSAUtils.privateEncrypt(valueAsString, RSAUtils.getPrivateKey(api_out_pri_key));
        log.info("入参加密后:{}", encodedData);
        String sign = RSASignUtil.sign(encodedData, api_out_pri_key, algorithm, encode);
        //组装请求报文参数
        requestData.setSecretKey(encodedData);
        requestData.setSign(sign);
        String requestEncodedData = objectMapper.writeValueAsString(requestData);
        //开始发送请求
        String doPostResult = doPost(requestEncodedData, jewelarEndorUrl);
        log.info("响应报文解密前:{}", doPostResult);
        RequestEncodedData responseData = objectMapper.readValue(doPostResult, RequestEncodedData.class);
        String secretKey = responseData.getSecretKey();
        //开始对响应报文的secretKey解密
        String decodedData = RSAUtils.publicDecrypt(secretKey, RSAUtils.getPublicKey(api_in_pub_key));
        log.info("响应报文解密后:{}", decodedData);
        //组装本地响应数据
        ResponseData data = objectMapper.readValue(decodedData, ResponseData.class);
        String signResult = RSASignUtil.sign(secretKey, api_out_pri_key, algorithm, encode);
        log.info("响应报文签名后:{}", sign);
        boolean check = RSASignUtil.verify(secretKey, signResult, api_out_pub_key, algorithm, encode);
        log.info("校验签名:{}\n", check);
        return data;
    }

    public String doPost(String requestEncodedData, String urlAddress) {
        byte[] postDataBytes = requestEncodedData.getBytes(StandardCharsets.UTF_8);
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.valueOf(MediaType.APPLICATION_FORM_URLENCODED_VALUE));
        headers.setContentLength(postDataBytes.length);
        // 构建HttpEntity
        HttpEntity<String> requestEntity = new HttpEntity<>(requestEncodedData, headers);
        // 发送POST请求
        ResponseEntity<String> responseEntity = restTemplate.exchange(urlAddress, HttpMethod.POST, requestEntity, String.class);
        return responseEntity.getBody();
    }
}
